package com.gitee.kamismile.igniteEx.spring.binding;

import com.gitee.kamismile.igniteEx.common.IginteConfigProperties;
import com.gitee.kamismile.igniteEx.ignite.ServiceHandle;
import com.gitee.kamismile.stone.commmon.exception.BusinessException;
import org.apache.commons.lang3.StringUtils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import java.io.Serializable;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

public class MapperProxy<T> implements InvocationHandler, Serializable {

    private static final long serialVersionUID = -6424540398559729838L;
    private final Class<T> mapperInterface;
    private final ServiceHandle serviceHandle;
    private final IginteConfigProperties iginteConfigProperties;
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    public MapperProxy(Class<T> mapperInterface, ServiceHandle serviceHandle, IginteConfigProperties iginteConfigProperties) {
        this.mapperInterface = mapperInterface;
        this.serviceHandle = serviceHandle;
        this.iginteConfigProperties = iginteConfigProperties;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        }

        String pages = mapperInterface.getPackage().getName();

        Map<String, IginteConfigProperties.ServerProvider> servers = iginteConfigProperties.getServers();
        List<String> serverName = servers.keySet().stream().map(v -> {
            List<Boolean> pageList = servers.get(v).getBasePackages().stream().map(j -> pages.contains(j)).filter(j -> j == true).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(pageList)) {
                return null;
            }
            return servers.get(v).getServerName();
        }).filter(v -> !Objects.isNull(v)).collect(Collectors.toList());
        T business = serviceHandle.getBusinessService(serverName.stream().findFirst().get(), StringUtils.uncapitalize(mapperInterface.getSimpleName()), mapperInterface);
        for (Method method1 : business.getClass().getMethods()) {
            if (method1.getName().equals(method.getName())) {
                return method1.invoke(business, args);
            }
        }

        throw new BusinessException("has no method");
    }


//    @UsesJava7
    private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
            throws Throwable {
        final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
                .getDeclaredConstructor(Class.class, int.class);
        if (!constructor.isAccessible()) {
            constructor.setAccessible(true);
        }
        final Class<?> declaringClass = method.getDeclaringClass();
        return constructor
                .newInstance(declaringClass,
                        MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
                                | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
                .unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
    }

    /**
     * Backport of java.lang.reflect.Method#isDefault()
     */
    private boolean isDefaultMethod(Method method) {
        return ((method.getModifiers()
                & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC)
                && method.getDeclaringClass().isInterface();
    }
}

